package proxy.db;

import proxy.Order;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//（注意）动态代理，这里虽然命名为订单动态代理，但是其实他可以代理任意类（只要把要代理的类作为构造方法的参数传进去就行了）
public class OrderServiceDynamicProxy implements InvocationHandler {

    private Object target;

    //这里传的对象 是Object 很巧妙，我们可以根据不同的类，
    public OrderServiceDynamicProxy(Object target) {
        this.target = target;
    }

    public Object bind(){
        Class cls = target.getClass();
        // 下面是动态代理的核心 （源码里面： 依据反射获取对应的构造参数，依据构造参数生成对应的实例）
        // 用反射，依据class 生成一个代理对象，并且返回。
        // 参数 ：1.cls.getClassLoader()是类的加载器；2.cls.getInterfaces()是传进去类的一组接口。  ==》返回的是 这个类的代理对象。
        /**
         loader: 用哪个类加载器去加载代理对象

         interfaces:动态代理类需要实现的接口

         h:动态代理方法在执行时（就是外部在调用saveOrder的时候），会调用h里面的invoke方法去执行
         * */
        return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
    }

    // 只要调用了OrderServiceDynamicProxy类里面的方法（target里面的方法） , 就会执行invoke方法。
    // method 是要被增强的方法。
    /**
     * proxy : 把代理对象自己传递进来
     * method：把代理对象当前调用的方法传递进来
     * args:把方法参数传递进来
     * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject = args[0];

        beforeMethod(argObject);

        System.out.println("外部的saveOrder 并不会执行，，而是invoke() 这里统一执行");
        /**
         * method.invoke(target,args); 就等同于外部的 orderServiceDynamicProxy.saveOrder(order);
         * */
        Object object = method.invoke(target,args);

        afterMethod();
        return object;
    }

    private void beforeMethod(Object obj){
        //todo here
        int userId = 0;
        //在动态代理里面 的beforeMethod 里面把数据库给确定了。
        System.out.println("动态代理 before core");
        if(obj instanceof Order){
            Order order = (Order)obj;
            userId = order.getUserId();
        }
        int dbRouter = userId %2;
        System.out.println("动态代理分配到【 db" + dbRouter + "】处理数据");

        //todo 设置dataSource
        DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));
    }

    private void afterMethod(){
        System.out.println("动态代理 after code");
    }
}